package top.dzygod.modules.oa.service.impl;

import com.google.common.io.ByteStreams;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import top.dzygod.annotation.Log;
import top.dzygod.config.FileProperties;
import top.dzygod.modules.oa.domain.LocalStorage;
import top.dzygod.modules.oa.repository.LocalStorageRepository;
import top.dzygod.modules.oa.service.LocalStorageService;
import top.dzygod.modules.oa.service.MinioService;
import top.dzygod.modules.oa.service.dto.LocalStorageDto;
import top.dzygod.modules.oa.service.dto.LocalStorageQueryCriteria;
import top.dzygod.modules.system.domain.User;
import top.dzygod.utils.FileUtil;
import top.dzygod.utils.SecurityUtils;
import top.dzygod.utils.StringUtils;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;

/**
 * @author dzygod
 */
@Service
@Slf4j
public class MinioServiceImpl implements MinioService {

    @Resource
    private MinioClient minioClient;
    @Resource
    private FileProperties properties;
    @Resource
    private LocalStorageRepository localStorageRepository;
    @Resource
    private LocalStorageService localStorageService;

    /********
     * Get file binary, generally, this file should be readable by browser.
     * @param bucketName
     * @param category
     * @param fileName
     * @return
     * @throws Exception
     */
    @SneakyThrows
    @Override
    public byte[] getFile(String bucketName, String category, String fileName) {
        String objectName = getObjectName(category, fileName);
        ObjectStat stat = minioClient.statObject(bucketName, objectName);
        int length = (int) stat.length();
        try (InputStream inputStream = minioClient.getObject(bucketName, objectName);
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream(length)) {
            ByteStreams.copy(inputStream, outputStream);
            byte[] buffer = outputStream.toByteArray();
            return buffer;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /*******
     * Stroe file to minio
     * @param bucketName
     * @param category
     * @param fileName
     * @param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean storeFile(String bucketName, String category, String fileName, MultipartFile file) {



        FileUtil.checkSize(properties.getMaxSize(), file.getSize());
        String suffix = FileUtil.getExtensionName(file.getOriginalFilename());
        String type = FileUtil.getFileType(suffix);

        String objectName = getObjectName(category, file.getOriginalFilename());
        checkBucketName(bucketName);

        // 查询文件是否已上传
        final LocalStorageQueryCriteria criteria = new LocalStorageQueryCriteria();
        criteria.setCategory(category);
        criteria.setRealName(file.getOriginalFilename());

        if (localStorageService.queryAll(criteria).parallelStream().count() >= 1) {
            throw new RuntimeException("该文件已存在，请修改文件名或删除原文件重新上传！");
        }

        try (InputStream inputStream = new ByteArrayInputStream(file.getBytes())) {
            PutObjectOptions options = new PutObjectOptions(file.getBytes().length, -1);
            minioClient.putObject(bucketName, objectName, inputStream, options);
        // mysql存储
        fileName = StringUtils.isBlank(fileName) ? FileUtil.getFileNameNoEx(file.getOriginalFilename()) : fileName;
        // user
        final User user = new User();
        user.setId(SecurityUtils.getCurrentUserId());
        LocalStorage localStorage = new LocalStorage(
                    file.getOriginalFilename(),
                    fileName,
                    category,
                    user,
                    suffix,
                    null,
                    type,
                    FileUtil.getSize(file.getSize())
            );
            localStorageRepository.save(localStorage);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
            return true;

    }

    @SneakyThrows
    @Override
    public void delete(String bucketName, String category, String fileName) {
        String objectName = getObjectName(category, fileName);
        minioClient.removeObject(bucketName, objectName);
    }

    /*****
     * Check existence of bucket, create it if it doesn't
     * @param bucketName
     */
    private void checkBucketName(String bucketName) {
        try {
            if (minioClient.bucketExists(bucketName)) {
                return;
            }

            log.info("Bucket {} doesn't exist, create", bucketName);
            minioClient.makeBucket(bucketName);
        } catch (Exception e) {
            log.error(e.toString());
        }

    }

    private String getObjectName(String category, String fileName) {
        return category + "/" + fileName;
    }
}
